﻿/*
 *  Copyright (c) 2012 Brandon Ellenberger
 *
 *  This file is part of OpSatPlan.
 *
 *  OpSatPlan is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpSatPlan is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpSatPlan.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace OpSatPlan
{
    public class Input
    {
        #region Members

        private Dictionary<string, string> mMap;
        private string mDomainFileName;
        private string mProblemFileName;
        private string mIdentifierFileName;
        private string mSolverName;
        private int mTimeLimit;
        private bool mVerbose;
        private int mNumberOfSteps;
        private bool mCnfOnly;

        #endregion

        #region Properties

        public int NumberOfSteps
        {
            get { return this.mNumberOfSteps; }
        }
        public bool CnfOnly
        {
            get { return this.mCnfOnly; }
        }
        public string DomainFileName
        {
            get { return this.mDomainFileName; }
        }

        public string ProblemFileName
        {
            get { return this.mProblemFileName; }
        }

        public string IdentifierFileName
        {
            get { return this.mIdentifierFileName; }
        }

        public string SolverName
        {
            get { return this.mSolverName; }
        }

        public int TimeLimit
        {
            get { return this.mTimeLimit; }
        }

        public int TimeLimitMilliseconds
        {
            get
            {
                return this.TimeLimit * 60000;
            }
        }

        public bool Verbose
        {
            get { return this.mVerbose; }
        }

        #endregion

        #region Constructor

        public Input(string[] args)
        {
            this.mMap = new Dictionary<string, string>();

            this.mMap.Add("-d", string.Empty);
            this.mMap.Add("-p", string.Empty);
            this.mMap.Add("-i", string.Empty);
            this.mMap.Add("-n", string.Empty);
            this.mMap.Add("-l", string.Empty);
            this.mMap.Add("-s", string.Empty);
            this.mMap.Add("-v", string.Empty);
            this.mMap.Add("-c", string.Empty);

            this.CheckParms(args);
        }

        #endregion

        public static void PrintUsage(System.IO.TextWriter writer)
        {
            writer.WriteLine("===================================================");
            writer.WriteLine("Usage: [...] is optional");
            writer.WriteLine("===================================================");
            writer.WriteLine("-p X (:string - problem filename)");
            writer.WriteLine("-d X (:string - domain filename)");
            writer.WriteLine("-i X (:string - identifier filename)");
            writer.WriteLine("[-n X (:integer - depth of planning graph)]");
            writer.WriteLine("[-l X (:integer - time limit minutes)]");
            writer.WriteLine("[-s X (:string - sat solver name; values: zchaff, precosat, minisat)]");
            writer.WriteLine("[-v X (:bool - verbose output 0 means OFF, 1 means ON)]");
            writer.WriteLine("[-c X (:bool - cnf only)]");
        }

        private void CheckParms(string[] args)
        {
            int i;
            string key;
            string val;

            for (i = 0; i < args.Length; i += 2)
            {
                key = args[i];
                Assertion.Assert<UnknownArgumentException>(this.mMap.ContainsKey(key), key + " is unknown argument");
                Assertion.Assert<UsageException>(this.mMap[key] == string.Empty, key + " is already defined");

                Assertion.Assert<UndefinedArgumentException>(i + 1 < args.Length, "No value defined for " + key);
                val = args[i + 1].Trim();
                Assertion.Assert<UndefinedArgumentException>(val != string.Empty, "No value defined for " + key);
                this.mMap[key] = val;
            }
            this.SetFields();
        }

        private void SetFields()
        {
            string val;

            val = this.mMap["-d"];
            Assertion.Assert<UndefinedArgumentException>(val != string.Empty, "Must define domain file name");
            this.mDomainFileName = val;

            val = this.mMap["-p"];
            Assertion.Assert<UndefinedArgumentException>(val != string.Empty, "Must define problem file name");
            this.mProblemFileName = val;

            val = this.mMap["-i"];
            Assertion.Assert<UndefinedArgumentException>(val != string.Empty, "Must define identifier file name");
            this.mIdentifierFileName = val;

            val = this.mMap["-n"];
            if (val == string.Empty)
            {
                this.mNumberOfSteps = int.MaxValue;
                this.mMap["-n"] = this.mNumberOfSteps.ToString();
            }
            else
            {
                Assertion.Assert<BadArgumentException>(int.TryParse(val, out this.mNumberOfSteps), "Mid point must be an integer");
            }

            val = this.mMap["-l"];
            if (val == string.Empty)
            {
                this.mTimeLimit = 30;
                this.mMap["-l"] = this.mTimeLimit.ToString();
            }
            else
            {
                Assertion.Assert<BadArgumentException>(int.TryParse(val, out this.mTimeLimit), "Time limit must be an integer");
            }

            val = this.mMap["-s"];
            if (val == string.Empty)
            {
                this.mSolverName = "cryptominisat";
                this.mMap["-s"] = this.mSolverName;
            }
            else
            {
                this.mSolverName = val;
            }

            val = this.mMap["-v"];
            if (val == string.Empty)
            {
                this.mVerbose = false;
                this.mMap["-v"] = "0";
            }
            else
            {
                int verbose;
                Assertion.Assert<BadArgumentException>(int.TryParse(val, out verbose), "Verbose must be 0 or 1");
                Assertion.Assert<BadArgumentException>(verbose == 0 || verbose == 1, "Verbose must be 0 or 1");
                this.mVerbose = Convert.ToBoolean(verbose);
            }

            val = this.mMap["-c"];
            if (val == string.Empty)
            {
                this.mCnfOnly = false;
                this.mMap["-c"] = "0";
            }
            else
            {
                int cnfOnly;
                Assertion.Assert<BadArgumentException>(int.TryParse(val, out cnfOnly), "Cnf Only must be 0 or 1");
                Assertion.Assert<BadArgumentException>(cnfOnly == 0 || cnfOnly == 1, "Cnf Only must be 0 or 1");
                this.mCnfOnly = Convert.ToBoolean(cnfOnly);
            }
        }
    }
}
